﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

namespace Dwarf.Managed
{
    /// <summary>
    /// Settings taken from command line and input file.
    /// </summary>
    class Settings
    {
        /// <summary>
        /// Input and output files.
        /// </summary>
        private String inputFile;
        private String profileFile;
        private String resultFile;


        /// <summary>
        /// Short dwarf's description.
        /// </summary>
        private const String DWARF_NAME = "Structured Grid, managed tpl kernel.";

        /// <summary>
        /// Default size for buffer.
        /// </summary>
        private const int BUFFER_SIZE = 1024;

        /// <summary>
        /// Constructor that init strings.
        /// </summary>
        public Settings()
        {
            inputFile = new String(new char[BUFFER_SIZE]);
            profileFile = new String(new char[BUFFER_SIZE]);
            resultFile = new String(new char[BUFFER_SIZE]);
        }

        /// <summary>
        /// Problem results output.
        /// </summary>
        /// <param name="sgsolver">
        /// Instance of Solver.
        /// </param>
        public void Finish(Solver sgsolver)
        {
            double time = 0;
            stop(ref time);

            Console.WriteLine();
            Console.WriteLine("Clock time (sec): {0} ", time.ToString("F8"));

            try
            {
                using (StreamWriter outputResult = File.CreateText(resultFile))
                {
                    using (StreamWriter outputProfile = File.CreateText(profileFile))
                    {
                        outputProfile.WriteLine("#Dwarfname:{0}", DWARF_NAME);

                        outputProfile.WriteLine("#Time: {0}", DateTime.Now.ToString());

                        outputProfile.WriteLine("#Dimensions: 2");
                        outputProfile.WriteLine("#Xmax: {0}", sgsolver.xmax);
                        outputProfile.WriteLine("#Ymax: {0}", sgsolver.ymax);
                        outputProfile.WriteLine("#Result time (sec): {0}", time.ToString("F8"));
                        outputResult.WriteLine("#Iterations: {0} #Result domain:", sgsolver.iterations);
                        for (int i = 0; i < sgsolver.ymax; i++)
                        {
                            for (int j = 0; j < sgsolver.xmax; j++)
                            {
                                outputResult.Write("{0} ", sgsolver.grid[i][j].ToString("F8"));
                            }
                            outputResult.WriteLine();
                        }
                        outputProfile.WriteLine("#eof");
                    }
                }
            }
            catch (Exception ex)
            {
                      Console.WriteLine(ex.Message);
                    throw new Exception("Error in output file");
            }
        }

        

        /// <summary>
        /// Init variables, parse cli params and input file.
        /// </summary>
        /// <param name="args">
        /// Cli params.
        /// </param>
        /// <param name="sgsolver">
        /// Instance of Solver.
        /// </param>
        public void Init(String[] args, Solver sgsolver)
        {
            //Parse cli params.
            parseArguments(
                         args.Length,
                         args,
                         ref inputFile,
                         ref profileFile,
                         ref resultFile);

            //Parse input file.
            parseConfigFile(sgsolver);

            // write settings if it's needed
            WriteSettings(sgsolver);
        }

        /// <summary>
        /// Start the time count.
        /// </summary>
        public void Start()
        {
            start();
        }

        /// <summary>
        /// Get one line from stream.
        /// </summary>

        private static String GetLine(StreamReader sr)
        {
            String s = sr.ReadLine();

            //Avoid comments lines.
            while (s.StartsWith("#") || s.StartsWith("//"))
            {
                s = sr.ReadLine();
            }

            return s;
        }

        /// <summary>
        /// Parse input file into solver's structures.
        /// </summary>
        /// <param name="sgsolver">
        /// Instance of Solver.
        /// </param>
        private void parseConfigFile(Solver sgsolver)
        {
            // Open the file to read from.
            try
            {
                using (StreamReader sr = File.OpenText(inputFile))
                {
                    sgsolver.epsilon = double.Parse(GetLine(sr));       //Get epsilon.
                    sgsolver.ymax = int.Parse(GetLine(sr));             //Get nomber of rows.
                    sgsolver.xmax = int.Parse(GetLine(sr));             //Get nomber of columns.

                    //Init matrices.
                    sgsolver.grid = new double[sgsolver.ymax][];
                    sgsolver.tempGrid = new double[sgsolver.ymax][];

                    //Get matrix.
                    String[] str;
                    for (int i = 0; i < sgsolver.ymax; i++)
                    {
                        sgsolver.grid[i] = new double[sgsolver.xmax];
                        sgsolver.tempGrid[i] = new double[sgsolver.xmax];

                        str = GetLine(sr).Split(' ');
                        for (int j = 0; j < sgsolver.xmax; j++)
                        {
                            sgsolver.grid[i][j] = sgsolver.tempGrid[i][j] = double.Parse(str[j]);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw new Exception("Error in input file");
            }
        }

        /// <summary>
        /// Write settings.
        /// </summary>
        /// <param name="sgsolver">
        /// Instance of Solver.
        /// </param>
        private void WriteSettings(Solver sgsolver)
        {
            // Write general settings.
            Console.WriteLine("Kernel settings summary : ");
            Console.WriteLine("Dwarf name              : {0} ", DWARF_NAME);
            Console.WriteLine("Epsilon                 : {0} ", sgsolver.epsilon.ToString("F8"));
            Console.WriteLine("Xmax                    : {0} ", sgsolver.xmax);
            Console.WriteLine("Ymax                    : {0} ", sgsolver.ymax);
            Console.WriteLine("Inputfile               : {0} ", inputFile);
            Console.WriteLine("Profilefile             : {0} ", profileFile);
            Console.WriteLine("Resultfile              : {0} ", resultFile);
            Console.WriteLine();
        }

        /// <summary>Parse the command line arguments and fill the reference parameters</summary>
        /// <param name="argc">Count of command line tokens</param>
        /// <param name="argv">Command line tokens</param>
        /// <param name="inputFile">Name of input text file</param>
        /// <param name="profileFile">Name of profile file</param>
        /// <param name="resultFile">Name of result file</param>
        [DllImport(@"CliTools.dll")]
        private static extern void parseArguments(
            int argc,
            String[] argv,
            ref string inputFile,
            ref string profileFile,
            ref string resultFile
            );

        /// <summary>
        /// Start the time count.
        /// </summary>
        [DllImport(@"CliTools.dll")]
        private static extern void start();

        /// <summary>
        /// Stop the time count.
        /// </summary>
        /// <param name="time">
        /// Value of the time.
        /// </param>
        [DllImport(@"CliTools.dll")]
        private static extern void stop(ref double time);
    }
}
